import { Injectable } from '@nestjs/common';
import { FindConditions, FindManyOptions, ObjectID, ObjectLiteral, Repository } from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';

type Where<T> = FindConditions<T>[] | FindConditions<T> | ObjectLiteral | string;
type Order<T> = {[key in keyof T]? : 'ASC' | 'DESC'}

@Injectable()
export class BaseModelService<T = ObjectLiteral> {
    protected primaryKey:keyof T;
    protected repository:Repository<T>;

    async querySql<E>(query: string) : Promise<E> {
        let res = await this.repository.query(query)
        return res
    }

    async pagination(pageNo:number|string, pageSize:number|string, where? : Where<T>, select? : Array<keyof T> | null, order? : Order<T>) {
        pageSize = Number(pageSize)
        return this.find(where, select, (Number(pageNo) - 1) * pageSize, pageSize, order)
    }

    async count(where? : Where<T>) {
        let total = await this.repository.count({
            where : where
        })
        
        return total
    }

    async value(where : Where<T>, field:keyof T) {
        const res = await this.findOne(where, [field])
        return res ? res[field] : undefined
    }

    async find(where? : Where<T>, select? : Array<keyof T> | null, skip? : number, take? : number, order? : Order<T>) {
        let options:FindManyOptions<T> = {
            
        }
        if(where) {
            options.where = where
        }
        if(select) {
            options.select = select
        }
        if(skip) {
            options.skip = skip
        }
        if(take) {
            options.take = take
        }
        if(order) {
            options.order = order
        }


        let res = await this.repository.find(options)
        return res
    }

    async findOne(where? : Where<T>, select? : Array<keyof T>, order? : Order<T>) {
        let res = await this.repository.findOne({
            select : select,
            where : where,
            order : order
        })

        return res ? res : null
    }

    async insert(data : QueryDeepPartialEntity<T> | (QueryDeepPartialEntity<T>[])) : Promise<number | null> {
        try {
            let res = await this.repository.insert(data)
            if(res) {
                let raw = res.raw
                if(raw && raw.affectedRows > 0) {
                    return raw.insertId
                }
            }
        } catch (error) {
            console.log(error)
        }
        
        return null
    }

    async update(where: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindConditions<T>, partialEntity: QueryDeepPartialEntity<T>) : Promise<number> {
        try {
            let res = await this.repository.update(where, partialEntity)
            return res.affected
        } catch (error) {
            console.log(error)
        }

        return 0
    }


    async delete(criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindConditions<T>) : Promise<number> {
        try {
            let res = await this.repository.delete(criteria)
            return res.affected
        } catch (error) {
            console.log(error)
        }

        return 0
    }

    async isExistsByWhere(where : Where<T>) :Promise<boolean>{
        let res = await this.findOne(where, [this.primaryKey])
        return res ? true : false
    }
}
